#include <stdlib.h>
#include <vector>
#include <algorithm>
#include "Platform.h"
#include "Scintilla.h"
#include "Selection.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

void SelectionPosition::MoveForInsertDelete( bool insertion, int startChange, int length ) {
  if( insertion ) {
    if( position == startChange ) {
      int virtualLengthRemove = std::min( length, virtualSpace );
      virtualSpace -= virtualLengthRemove;
      position += virtualLengthRemove;
    } else if( position > startChange ) {
      position += length;
    }
  } else {
    if( position == startChange ) {
      virtualSpace = 0;
    }
    if( position > startChange ) {
      int endDeletion = startChange + length;
      if( position > endDeletion ) {
        position -= length;
      } else {
        position = startChange;
        virtualSpace = 0;
      }
    }
  }
}

bool SelectionPosition::operator <( const SelectionPosition &other ) const {
  if( position == other.position ) {
    return virtualSpace < other.virtualSpace;
  } else {
    return position < other.position;
  }
}

bool SelectionPosition::operator >( const SelectionPosition &other ) const {
  if( position == other.position ) {
    return virtualSpace > other.virtualSpace;
  } else {
    return position > other.position;
  }
}

bool SelectionPosition::operator <=( const SelectionPosition &other ) const {
  if( position == other.position && virtualSpace == other.virtualSpace ) {
    return true;
  } else {
    return other > *this;
  }
}

bool SelectionPosition::operator >=( const SelectionPosition &other ) const {
  if( position == other.position && virtualSpace == other.virtualSpace ) {
    return true;
  } else {
    return *this > other;
  }
}

int SelectionRange::Length() const {
  if( anchor > caret ) {
    return anchor.Position() - caret.Position();
  } else {
    return caret.Position() - anchor.Position();
  }
}

void SelectionRange::MoveForInsertDelete( bool insertion, int startChange, int length ) {
  caret.MoveForInsertDelete( insertion, startChange, length );
  anchor.MoveForInsertDelete( insertion, startChange, length );
}

bool SelectionRange::Contains( int pos ) const {
  if( anchor > caret ) {
    return ( pos >= caret.Position() ) && ( pos <= anchor.Position() );
  } else {
    return ( pos >= anchor.Position() ) && ( pos <= caret.Position() );
  }
}

bool SelectionRange::Contains( SelectionPosition sp ) const {
  if( anchor > caret ) {
    return ( sp >= caret ) && ( sp <= anchor );
  } else {
    return ( sp >= anchor ) && ( sp <= caret );
  }
}

bool SelectionRange::ContainsCharacter( int posCharacter ) const {
  if( anchor > caret ) {
    return ( posCharacter >= caret.Position() ) && ( posCharacter < anchor.Position() );
  } else {
    return ( posCharacter >= anchor.Position() ) && ( posCharacter < caret.Position() );
  }
}

SelectionSegment SelectionRange::Intersect( SelectionSegment check ) const {
  SelectionSegment inOrder( caret, anchor );
  if( ( inOrder.start <= check.end ) || ( inOrder.end >= check.start ) ) {
    SelectionSegment portion = check;
    if( portion.start < inOrder.start ) {
      portion.start = inOrder.start;
    }
    if( portion.end > inOrder.end ) {
      portion.end = inOrder.end;
    }
    if( portion.start > portion.end ) {
      return SelectionSegment();
    } else
    { return portion; }
  } else {
    return SelectionSegment();
  }
}

bool SelectionRange::Trim( SelectionRange range ) {
  SelectionPosition startRange = range.Start();
  SelectionPosition endRange = range.End();
  SelectionPosition start = Start();
  SelectionPosition end = End();
  PLATFORM_ASSERT( start <= end );
  PLATFORM_ASSERT( startRange <= endRange );
  if( ( startRange <= end ) && ( endRange >= start ) ) {
    if( ( start > startRange ) && ( end < endRange ) ) {
      // Completely covered by range -> empty at start
      end = start;
    } else if( ( start < startRange ) && ( end > endRange ) ) {
      // Completely covers range -> empty at start
      end = start;
    } else if( start <= startRange ) {
      // Trim end
      end = startRange;
    } else { //
      PLATFORM_ASSERT( end >= endRange );
      // Trim start
      start = endRange;
    }
    if( anchor > caret ) {
      caret = start;
      anchor = end;
    } else {
      anchor = start;
      caret = end;
    }
    return Empty();
  } else {
    return false;
  }
}

// If range is all virtual collapse to start of virtual space
void SelectionRange::MinimizeVirtualSpace() {
  if( caret.Position() == anchor.Position() ) {
    int virtualSpace = caret.VirtualSpace();
    if( virtualSpace > anchor.VirtualSpace() ) {
      virtualSpace = anchor.VirtualSpace();
    }
    caret.SetVirtualSpace( virtualSpace );
    anchor.SetVirtualSpace( virtualSpace );
  }
}

Selection::Selection() : mainRange( 0 ), moveExtends( false ), tentativeMain( false ), selType( selStream ) {
  AddSelection( SelectionRange( SelectionPosition( 0 ) ) );
}

Selection::~Selection() {
}

bool Selection::IsRectangular() const {
  return ( selType == selRectangle ) || ( selType == selThin );
}

int Selection::MainCaret() const {
  return ranges[mainRange].caret.Position();
}

int Selection::MainAnchor() const {
  return ranges[mainRange].anchor.Position();
}

SelectionRange &Selection::Rectangular() {
  return rangeRectangular;
}

SelectionSegment Selection::Limits() const {
  if( ranges.empty() ) {
    return SelectionSegment();
  } else {
    SelectionSegment sr( ranges[0].anchor, ranges[0].caret );
    for( size_t i = 1; i < ranges.size(); i++ ) {
      sr.Extend( ranges[i].anchor );
      sr.Extend( ranges[i].caret );
    }
    return sr;
  }
}

SelectionSegment Selection::LimitsForRectangularElseMain() const {
  if( IsRectangular() ) {
    return Limits();
  } else {
    return SelectionSegment( ranges[mainRange].caret, ranges[mainRange].anchor );
  }
}

size_t Selection::Count() const {
  return ranges.size();
}

size_t Selection::Main() const {
  return mainRange;
}

void Selection::SetMain( size_t r ) {
  PLATFORM_ASSERT( r < ranges.size() );
  mainRange = r;
}

SelectionRange &Selection::Range( size_t r ) {
  return ranges[r];
}

const SelectionRange &Selection::Range( size_t r ) const {
  return ranges[r];
}

SelectionRange &Selection::RangeMain() {
  return ranges[mainRange];
}

const SelectionRange &Selection::RangeMain() const {
  return ranges[mainRange];
}

SelectionPosition Selection::Start() const {
  if( IsRectangular() ) {
    return rangeRectangular.Start();
  } else {
    return ranges[mainRange].Start();
  }
}

bool Selection::MoveExtends() const {
  return moveExtends;
}

void Selection::SetMoveExtends( bool moveExtends_ ) {
  moveExtends = moveExtends_;
}

bool Selection::Empty() const {
  for( size_t i = 0; i < ranges.size(); i++ ) {
    if( !ranges[i].Empty() ) {
      return false;
    }
  }
  return true;
}

SelectionPosition Selection::Last() const {
  SelectionPosition lastPosition;
  for( size_t i = 0; i < ranges.size(); i++ ) {
    if( lastPosition < ranges[i].caret ) {
      lastPosition = ranges[i].caret;
    }
    if( lastPosition < ranges[i].anchor ) {
      lastPosition = ranges[i].anchor;
    }
  }
  return lastPosition;
}

int Selection::Length() const {
  int len = 0;
  for( size_t i = 0; i < ranges.size(); i++ ) {
    len += ranges[i].Length();
  }
  return len;
}

void Selection::MovePositions( bool insertion, int startChange, int length ) {
  for( size_t i = 0; i < ranges.size(); i++ ) {
    ranges[i].MoveForInsertDelete( insertion, startChange, length );
  }
  if( selType == selRectangle ) {
    rangeRectangular.MoveForInsertDelete( insertion, startChange, length );
  }
}

void Selection::TrimSelection( SelectionRange range ) {
  for( size_t i = 0; i < ranges.size(); ) {
    if( ( i != mainRange ) && ( ranges[i].Trim( range ) ) ) {
      // Trimmed to empty so remove
      for( size_t j = i; j < ranges.size() - 1; j++ ) {
        ranges[j] = ranges[j + 1];
        if( j == mainRange - 1 ) {
          mainRange--;
        }
      }
      ranges.pop_back();
    } else
    { i++; }
  }
}

void Selection::SetSelection( SelectionRange range ) {
  ranges.clear();
  ranges.push_back( range );
  mainRange = ranges.size() - 1;
}

void Selection::AddSelection( SelectionRange range ) {
  TrimSelection( range );
  ranges.push_back( range );
  mainRange = ranges.size() - 1;
}

void Selection::AddSelectionWithoutTrim( SelectionRange range ) {
  ranges.push_back( range );
  mainRange = ranges.size() - 1;
}

void Selection::DropSelection( size_t r ) {
  if( ( ranges.size() > 1 ) && ( r < ranges.size() ) ) {
    size_t mainNew = mainRange;
    if( mainNew >= r ) {
      if( mainNew == 0 ) {
        mainNew = ranges.size() - 2;
      } else
      { mainNew--; }
    }
    ranges.erase( ranges.begin() + r );
    mainRange = mainNew;
  }
}

void Selection::TentativeSelection( SelectionRange range ) {
  if( !tentativeMain ) {
    rangesSaved = ranges;
  }
  ranges = rangesSaved;
  AddSelection( range );
  TrimSelection( ranges[mainRange] );
  tentativeMain = true;
}

void Selection::CommitTentative() {
  rangesSaved.clear();
  tentativeMain = false;
}

int Selection::CharacterInSelection( int posCharacter ) const {
  for( size_t i = 0; i < ranges.size(); i++ ) {
    if( ranges[i].ContainsCharacter( posCharacter ) ) {
      return i == mainRange ? 1 : 2;
    }
  }
  return 0;
}

int Selection::InSelectionForEOL( int pos ) const {
  for( size_t i = 0; i < ranges.size(); i++ ) {
    if( !ranges[i].Empty() && ( pos > ranges[i].Start().Position() ) && ( pos <= ranges[i].End().Position() ) ) {
      return i == mainRange ? 1 : 2;
    }
  }
  return 0;
}

int Selection::VirtualSpaceFor( int pos ) const {
  int virtualSpace = 0;
  for( size_t i = 0; i < ranges.size(); i++ ) {
    if( ( ranges[i].caret.Position() == pos ) && ( virtualSpace < ranges[i].caret.VirtualSpace() ) ) {
      virtualSpace = ranges[i].caret.VirtualSpace();
    }
    if( ( ranges[i].anchor.Position() == pos ) && ( virtualSpace < ranges[i].anchor.VirtualSpace() ) ) {
      virtualSpace = ranges[i].anchor.VirtualSpace();
    }
  }
  return virtualSpace;
}

void Selection::Clear() {
  ranges.clear();
  ranges.push_back( SelectionRange() );
  mainRange = ranges.size() - 1;
  selType = selStream;
  moveExtends = false;
  ranges[mainRange].Reset();
  rangeRectangular.Reset();
}

void Selection::RemoveDuplicates() {
  for( size_t i = 0; i < ranges.size() - 1; i++ ) {
    if( ranges[i].Empty() ) {
      size_t j = i + 1;
      while( j < ranges.size() ) {
        if( ranges[i] == ranges[j] ) {
          ranges.erase( ranges.begin() + j );
          if( mainRange >= j ) {
            mainRange--;
          }
        } else
        { j++; }
      }
    }
  }
}

void Selection::RotateMain() {
  mainRange = ( mainRange + 1 ) % ranges.size();
}

